﻿namespace Ex03.GarageManagementSystem.ConsoleUI
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using GarageLogic;

    /// <summary>
    /// This class managed the interface between the console and the logic part
    /// </summary>
    public class GarageManager
    {
        #region Messages
        private const string k_WelcomeMessage = "~~~Hello! Welcome to the garage manager system~~~";
        private const string k_MenuMessage = 
@"1. Add new vehicle.
2. Print all vehicles licensing numbers and states.
3. Change vehicle state.
4. Inflate tire to maximum.
5. Fuel vehicle.
6. Charge electric vehicle.
7. Print all data of vehicle by licensing number.
8. Exit Program.";

        private const string k_EnterYourChoiceMsg = "Please enter your choice: ";
        private const string k_AcceptLicensingNumberMsg = "Please enter your driving license number:";
        private const string k_AcceptNewVehicleStateMsg = 
@"Choose vehicle state:
1. In repair.
2. Repaired.
3. Paid.";

        private const int k_NoFiltering = 0;
        private const string k_AskForFilterByStateMsg = "Do you want to filter by state? (0 - No filtering, 1 - In repair, 2 - Repaired, 3 - Paid)";
        private const string k_AskForMinutsToChargeElectricCasMsg = "Enter how much time you want to charge the car (in minuts):";
        private const string k_AskForFuelTypeMsg =
@"Choose type fuel:
1. Soler.
2. Octan 98.
3. Octan 96.
4. Octan 95.";

        private const string k_AskForAmountOfLittersToRefuelMsg = "How much litters to refuel? (in litters)";
        private const string k_GetVehicletypeMsg =
@"Choose vehicle type:
1. Motorcycle.
2. Car.
3. Truck.";

        private const string k_GetEngineTypeMsg =
@"Choose engine type:
1. Fuel.
2. Electric.";

        private const string k_VehicleStateChangedMsg = "Current vehicle state changed to {0}.";
        private const string k_TypeNotValidMsg = "One of the types you entered is not valid. Try again";
        private const string k_VehicleInflatedMaximumMsg = "The vehicle inflated to maximum";
        private const string k_VehicleCreationDoneMsg = "The vehicle was entred succesfully.";
        private const string k_GoodByeMsg = "Goodbye!!";
        private const string k_ArgumentExceptionMsg = "Error: one of the arguments is not valid";
        private const string k_NotLegalOptionChosen = "There is no such option. Please choose again: ";
        private const string k_NotLegalStateChosen = "There is no such state. Please choose again: ";
        private const string k_AskForMinutsToAddMsg = "How much time to charge (in minuts)?";
        private const string k_RefuelSucceededMsg = "Refuel was done.";
        private const string k_ChargeSucceededMsg = "Charge was done.";
        private const string k_VehicleNotFoundMsg = "Vehicle not found. Try again";
        private const string k_PressKey = "Press any key to continue...";
        private const string k_StateIsNotValid = @"The state is not valid.";
        private const string k_LicenseNotExists = @"The License doesn't exist in system.";
        private const string k_FuelTypeDoesntExists = @"The fuel type doesn't exist in system.";
        private const string k_VehicleTypeDoesntExists = @"The vehicle type doesn't exist in system.";
        private const string k_EngineTypeDoesntExists = @"The engine type doesn't exist in system.";
        private const string k_NoVehiclesWereFound = "No vehicles were found.";
        private const string k_FilterByStateTitle = "License No. - State";
        private const string k_FilterByStateDisplayPair = "{0} - {1}";
        #endregion

        #region Data Members
        private readonly GarageLogic r_GarageLogic;
        #endregion

        #region Enums
        private enum eMenuChoice
        {
            NotChosenYet,
            AddNewVehicle,
            PrintAllVehiclesLicensingNumbers,
            ChangeVehicleState,
            BlowUpTire,
            RefuelGasVehicle,
            RefuelElectricVehicle,
            PrintAllVehicleData,
            ExitProgram
        }
        #endregion 

        #region Constructor
        public GarageManager()
        {
            r_GarageLogic = new GarageLogic();
        }
        #endregion

        #region Public Methods
        public void StartManageGarage()
        {
            welcomeUser();
            printMenu();
            readUserChoice();
        }
        #endregion

        #region Private Methods
        private void welcomeUser()
        {
            Console.WriteLine(k_WelcomeMessage);
        }

        private void printMenu()
        {
            StringBuilder screenMenu = new StringBuilder();

            Console.Clear();
            screenMenu.AppendLine(k_MenuMessage);
            screenMenu.AppendLine(k_EnterYourChoiceMsg);
            Console.Write(screenMenu);
        }

        private void readUserChoice()
        {
            bool goodInput = false;
            eMenuChoice userMenuChoice = eMenuChoice.NotChosenYet;

            do
            {
                try
                {
                    userMenuChoice = (eMenuChoice)Enum.Parse(typeof(eMenuChoice), Console.ReadLine());
                    if (!Enum.IsDefined(typeof(eMenuChoice), userMenuChoice))
                    {
                        Console.WriteLine(k_NotLegalOptionChosen);
                    }
                    else
                    {
                        goodInput = true;
                        Console.Clear();
                        executeUserChoice(userMenuChoice);
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine(k_NotLegalOptionChosen);
                }
            }
            while (!goodInput || userMenuChoice != eMenuChoice.ExitProgram);
        }

        private void executeUserChoice(eMenuChoice i_userMenuChoice)
        {
            switch (i_userMenuChoice)
            {
                case eMenuChoice.AddNewVehicle:
                    addNewVehicle();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.BlowUpTire:
                    inflateTiresToMaximum();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.ChangeVehicleState:
                    changeVehicleState();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.PrintAllVehicleData:
                    printAllVehicleData();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.PrintAllVehiclesLicensingNumbers:
                    printAllVehicleLicensingNumbers();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.RefuelElectricVehicle:
                    refuelElectricVehicle();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.RefuelGasVehicle:
                    refuelGasVehicle();
                    pressAnyKey();
                    printMenu();
                    break;
                case eMenuChoice.ExitProgram:
                    Console.WriteLine(k_GoodByeMsg);
                    pressAnyKey();
                    break;
                default:
                    Console.WriteLine(k_NotLegalOptionChosen);
                    break;
            }
        }

        private void addNewVehicle()
        {
            bool newGarageVehicleWasAdded = false;
            string licenseNumber;
            List<string> vehicleOutputMessages;

            licenseNumber = acceptLicenseNumber();

            if (r_GarageLogic.IsFoundInGarageByLicenseNumber(licenseNumber))
            {
                r_GarageLogic.ChangeVehicleStateByLicense(licenseNumber, eVehicleState.InRepair);
                Console.WriteLine(k_VehicleStateChangedMsg, eVehicleState.InRepair.ToString());
            }
            else
            {
                while (newGarageVehicleWasAdded.Equals(false))
                {
                    newGarageVehicleWasAdded = addNewVehicleByType(licenseNumber);
                }

                vehicleOutputMessages = r_GarageLogic.GetNeededParamsToBeUpdateToGarageVehicle(licenseNumber);
                fillCurrentVehicleMessages(licenseNumber, vehicleOutputMessages);
                Console.WriteLine(k_VehicleCreationDoneMsg);
            }
        }

        private bool addNewVehicleByType(string i_NewLicenseNumber)
        {
            bool result = false;
            eVehicleType vehicleType;
            eEngineType engineType;

            if (acceptVehicleTypeAndWriteIfError(out vehicleType).Equals(true) && 
                acceptEngineTypeAndWriteIfError(out engineType).Equals(true))
            {
                try
                {
                    r_GarageLogic.AddNewGarageVehicle(i_NewLicenseNumber, vehicleType, engineType);
                    result = true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }

            return result;
        }

        private void fillCurrentVehicleMessages(string i_LicenseNumber, List<string> i_MessagesDisplayList)
        {
            Dictionary<string, string> filledParametersDictionary = new Dictionary<string, string>();
            bool allParametersWereUpdated = false;

            do
            {
                foreach (string parameter in i_MessagesDisplayList)
                {
                    if (parameterDisplayAppearsForTheFirstTime(parameter, ref filledParametersDictionary))
                    {
                        Console.WriteLine(parameter);
                        filledParametersDictionary.Add(parameter, Console.ReadLine());
                    }
                    else if (parameterWasntSetAfterLastSet(parameter, ref filledParametersDictionary))
                    {
                        Console.WriteLine(parameter);
                        filledParametersDictionary[parameter] = Console.ReadLine();
                    }
                }

                try
                {
                    allParametersWereUpdated = r_GarageLogic.SetNeededParamsToBeUpdateToGarageVehicle(i_LicenseNumber, ref filledParametersDictionary);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            } 
            while (allParametersWereUpdated.Equals(false));
        }

        private bool parameterDisplayAppearsForTheFirstTime(string i_Parameter, ref Dictionary<string, string> i_ParametersDisplayList)
        {
            return !i_ParametersDisplayList.ContainsKey(i_Parameter);
        }

        private bool parameterWasntSetAfterLastSet(string i_Parameter, ref Dictionary<string, string> i_ParametersDisplayList)
        {
            return i_ParametersDisplayList[i_Parameter] != string.Empty && i_ParametersDisplayList[i_Parameter] != null;
        }

        private void inflateTiresToMaximum()
        {
            string licenseNumber;

            if (acceptLicenseAndWriteIfError(out licenseNumber).Equals(true))
            {
                try
                {
                    r_GarageLogic.InflateToMaximum(licenseNumber);
                    Console.WriteLine(k_VehicleInflatedMaximumMsg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        private void changeVehicleState()
        {
            string licenseNumber;
            eVehicleState newVehicleState;

            if (acceptLicenseAndWriteIfError(out licenseNumber).Equals(true) && 
                acceptVehicleStateAndWriteIfError(out newVehicleState).Equals(true))
            {
                try
                {
                    r_GarageLogic.ChangeVehicleStateByLicense(licenseNumber, newVehicleState);
                    Console.WriteLine(k_VehicleStateChangedMsg, newVehicleState.ToString());
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
               
        private void printAllVehicleData()
        {
            string licenseNumber;
            List<string> garageVehicleParametersToDisplay;

            try
            {
                if (acceptLicenseAndWriteIfError(out licenseNumber).Equals(true))
                {
                    garageVehicleParametersToDisplay = r_GarageLogic.GetGarageVehicleDetailsByLicenseNumber(licenseNumber);

                    foreach (string parameter in garageVehicleParametersToDisplay)
                    {
                        Console.WriteLine(parameter);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private void printAllVehicleLicensingNumbers()
        {
            bool allParametersWereAccepted = false;
            bool filterByState = false;
            eVehicleState stateChosen = eVehicleState.InRepair;

            do
            {
                allParametersWereAccepted = acceptStateToFilterAndWriteIfError(out filterByState, out stateChosen);
            }
            while (!allParametersWereAccepted);

            Dictionary<string, string> DicOfVehicle = r_GarageLogic.GetListOfGarageVehicles(filterByState, stateChosen);
            PrintLicenseAndStateByDictionary(DicOfVehicle); 
        }

        private void PrintLicenseAndStateByDictionary(Dictionary<string, string> i_DictionaryOfLicensesAndStates)
        {
            StringBuilder builder = new StringBuilder();

            if (i_DictionaryOfLicensesAndStates.Count > 0)
            {
                builder.AppendLine(k_FilterByStateTitle);
                builder.AppendLine(new string('-', 30));

                foreach (var pair in i_DictionaryOfLicensesAndStates)
                {
                    builder.AppendLine(string.Format(k_FilterByStateDisplayPair, pair.Key, pair.Value));
                }
            }
            else
            {
                builder.AppendLine(k_NoVehiclesWereFound);
            }

            Console.Write(builder);
        }

        private void refuelElectricVehicle()
        {
            string licenseNumber;
            float minutesToCharge;

            if (acceptLicenseAndWriteIfError(out licenseNumber).Equals(true) && 
                acceptMinutesToChargeAndWriteIfError(out minutesToCharge).Equals(true))
            {
                try
                {
                    r_GarageLogic.RechargeElectricVehicleByLicenseNumberAndEnergyAddition(licenseNumber, minutesToCharge);
                    Console.WriteLine(k_ChargeSucceededMsg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        private void refuelGasVehicle()
        {
            string licenseNumber;
            float howMuchToAdd;
            eFuelType fuelType;

            if (acceptLicenseAndWriteIfError(out licenseNumber).Equals(true) && 
                acceptFuelTypeAndWriteIfError(out fuelType).Equals(true) && 
                acceptFuelAmountAndWriteIfError(out howMuchToAdd).Equals(true))
            {
                try
                {
                    r_GarageLogic.RefuelVehicleByLicenseNumberAndEnergyAddition(licenseNumber, howMuchToAdd, fuelType);
                    Console.WriteLine(k_RefuelSucceededMsg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        private void pressAnyKey()
        {
            Console.WriteLine(k_PressKey);
            Console.ReadKey();
        }

        private string acceptLicenseNumber()
        {
            Console.WriteLine(k_AcceptLicensingNumberMsg);
            return Console.ReadLine();
        }

        private bool acceptFuelTypeAndWriteIfError(out eFuelType o_FuelType)
        {
            bool result = false;
            int intFuelType;
            o_FuelType = eFuelType.Octan95;
            eFuelType newFuelType;

            Console.WriteLine(k_AskForFuelTypeMsg);
            bool goodInput = int.TryParse(Console.ReadLine(), out intFuelType);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            if (result.Equals(true))
            {
                newFuelType = (eFuelType)intFuelType;
                if (!eFuelType.IsDefined(typeof(eFuelType), newFuelType))
                {
                    Console.WriteLine(k_FuelTypeDoesntExists);
                    result = false;
                }
                else
                {
                    o_FuelType = newFuelType;
                    result = true;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }

        private bool acceptFuelAmountAndWriteIfError(out float o_FuelAmount)
        {
            bool result = true;

            Console.WriteLine(k_AskForAmountOfLittersToRefuelMsg);
            bool goodInput = float.TryParse(Console.ReadLine(), out o_FuelAmount);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            return result;
        }

        private bool acceptStateToFilterAndWriteIfError(out bool o_ToFilter, out eVehicleState o_StateToFilter)
        {
            bool result = false;
            int intStateToFilter;
            o_ToFilter = false;
            o_StateToFilter = eVehicleState.InRepair;
            eVehicleState newStateToFilter;

            Console.WriteLine(k_AskForFilterByStateMsg);
            bool goodInput = int.TryParse(Console.ReadLine(), out intStateToFilter);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            if (result.Equals(true))
            {
                newStateToFilter = (eVehicleState)intStateToFilter;
                if (!eVehicleState.IsDefined(typeof(eVehicleState), newStateToFilter) && intStateToFilter != k_NoFiltering)
                {
                    Console.WriteLine(k_NotLegalStateChosen);
                    result = false;
                }
                else
                {
                    o_ToFilter = intStateToFilter == k_NoFiltering ? false : true;
                    o_StateToFilter = newStateToFilter;
                    result = true;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }

        private bool acceptMinutesToChargeAndWriteIfError(out float o_MinutesToCharge)
        {
            bool result = false;

            Console.WriteLine(k_AskForMinutsToAddMsg);
            bool goodInput = float.TryParse(Console.ReadLine(), out o_MinutesToCharge);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            return result;
        }

        private bool acceptVehicleStateAndWriteIfError(out eVehicleState o_VehicleState)
        {
            bool result = false;
            int intNewState;
            o_VehicleState = eVehicleState.InRepair;
            eVehicleState newVehicleState;

            Console.WriteLine(k_AcceptNewVehicleStateMsg);
            bool goodInput = int.TryParse(Console.ReadLine(), out intNewState);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            if (result.Equals(true))
            {
                newVehicleState = (eVehicleState)intNewState;
                if (!eVehicleState.IsDefined(typeof(eVehicleState), newVehicleState))
                {
                    Console.WriteLine(k_StateIsNotValid);
                    result = false;
                }
                else
                {
                    o_VehicleState = newVehicleState;
                    result = true;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }

        private bool checkIfInputIsGoodAndWriteIfError(bool i_Input)
        {
            bool result = false;

            if (i_Input.Equals(false))
            {
                Console.WriteLine(k_TypeNotValidMsg);
                result = false;
            }
            else
            {
                result = true;
            }

            return result;
        }

        private bool acceptLicenseAndWriteIfError(out string o_LicenseNumber)
        {
            o_LicenseNumber = acceptLicenseNumber();
            bool result = false;

            if (!r_GarageLogic.IsFoundInGarageByLicenseNumber(o_LicenseNumber))
            {
                Console.WriteLine(k_LicenseNotExists);
                result = false;
            }
            else
            {
                result = true;
            }

            return result;
        }

        private bool acceptEngineTypeAndWriteIfError(out eEngineType o_EngineType)
        {
            bool result = false;
            int intEngineType;
            o_EngineType = eEngineType.Electric;
            eEngineType newEngineType;

            Console.WriteLine(k_GetEngineTypeMsg);
            bool goodInput = int.TryParse(Console.ReadLine(), out intEngineType);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            if (result.Equals(true))
            {
                newEngineType = (eEngineType)intEngineType;
                if (!eEngineType.IsDefined(typeof(eEngineType), newEngineType))
                {
                    Console.WriteLine(k_EngineTypeDoesntExists);
                    result = false;
                }
                else
                {
                    o_EngineType = newEngineType;
                    result = true;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }

        private bool acceptVehicleTypeAndWriteIfError(out eVehicleType o_VehicleType)
        {
            bool result = false;
            int intVehicleType;
            o_VehicleType = eVehicleType.Car;
            eVehicleType newVehicleType;

            Console.WriteLine(k_GetVehicletypeMsg);
            bool goodInput = int.TryParse(Console.ReadLine(), out intVehicleType);

            result = checkIfInputIsGoodAndWriteIfError(goodInput);

            if (result.Equals(true))
            {
                newVehicleType = (eVehicleType)intVehicleType;
                if (!eVehicleType.IsDefined(typeof(eVehicleType), newVehicleType))
                {
                    Console.WriteLine(k_VehicleTypeDoesntExists);
                    result = false;
                }
                else
                {
                    o_VehicleType = newVehicleType;
                    result = true;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }
        #endregion
    }
}
